﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using CsvSerializer;
using CsvSerializer.CsvReader;
using CsvSerializer.CsvWriter;
using Newtonsoft.Json;
using ProtoBuf;

namespace dse_pe_parser
{
    public class PeRatiosList : IPersistable
    {
        private readonly Dictionary<string, PeRatio> _dictRecords = new Dictionary<string, PeRatio>();

        public List<PeRatio> PeRatios
        {
            get { return _dictRecords.Values.ToList(); }
        }

        public List<string> Symbols
        {
            get { return _dictRecords.Keys.ToList(); }
        }

        public PeRatio this[string index]
        {
            get { return _dictRecords[index.Trim().ToUpper()]; }
        }

        #region IPersistable Members

        public string ToCsv()
        {
            using (var memoryStream = new MemoryStream())
            using (var streamWriter = new StreamWriter(memoryStream))
            using (var streamReader = new StreamReader(memoryStream))
            {
                var options = new CsvWriterOptions
                                  {
                                      Delimiter = GlobalOptions.Instance.CsvDelimiterChar,
                                      HasHeaderRecord = GlobalOptions.Instance.CsvHasHeader,
                                      Quote = GlobalOptions.Instance.CsvQuoteChar
                                  };

                using (var writer = new CsvWriter(streamWriter, options))
                {
                    writer.WriteRecords(PeRatios);
                    memoryStream.Position = 0;
                    return streamReader.ReadToEnd();
                }
            }
        }

        public string ToXml()
        {
            string xmlString = null;
            try
            {
                using (var stream = new MemoryStream())
                {
                    var settings = new XmlWriterSettings
                                       {
                                           Indent = GlobalOptions.Instance.IndentSerializedOutput,
                                           OmitXmlDeclaration = true,
                                           Encoding = Encoding.UTF8,
                                           ConformanceLevel = ConformanceLevel.Auto
                                       };

                    using (XmlWriter writer = XmlWriter.Create(stream, settings))
                    {
                        var serializer = new XmlSerializer(typeof (List<PeRatio>));
                        serializer.Serialize(writer, PeRatios);
                        writer.Flush();
                    }
                    xmlString = Encoding.UTF8.GetString(stream.ToArray());
                }
            }
            catch
            {
            }

            return xmlString;
        }

        public string ToJson()
        {
            return JsonConvert.SerializeObject(PeRatios,
                                               GlobalOptions.Instance.IndentSerializedOutput
                                                   ? Newtonsoft.Json.Formatting.Indented
                                                   : Newtonsoft.Json.Formatting.None);
        }

        public byte[] ToProtobuf()
        {
            byte[] proto;
            using (var stream = new MemoryStream())
            {
                Serializer.Serialize(stream, PeRatios);
                proto = stream.ToArray();
            }

            return proto;
        }

        IPersistable IPersistable.FromCsv(string csv, bool containsHeader)
        {
            return FromCsv(csv, containsHeader);
        }

        IPersistable IPersistable.FromXml(string xml)
        {
            return FromXml(xml);
        }

        IPersistable IPersistable.FromJson(string json)
        {
            return FromJson(json);
        }

        IPersistable IPersistable.FromProtobuf(byte[] proto)
        {
            return FromProtobuf(proto);
        }

        #endregion

        public void AddPeRatio(string symbol, DateTime date, decimal peLatest, decimal peAudited)
        {
            symbol = symbol.Trim().ToUpperInvariant();
            var pe = new PeRatio
                         {
                             EffectiveDate = date,
                             Symbol = symbol,
                             PeLatest = peLatest,
                             PeAudited = peAudited
                         };
            _dictRecords.Add(symbol, pe);
        }

        private void addItem(PeRatio pe)
        {
            _dictRecords.Add(pe.Symbol, pe);
        }

        private static PeRatiosList createPeRatiosList(IEnumerable<PeRatio> list)
        {
            var pelist = new PeRatiosList();

            foreach (var pe in list)
            {
                pe.Symbol = pe.Symbol.Trim().ToUpperInvariant();
                pelist.addItem(pe);
            }

            return pelist;
        }

        public static PeRatiosList FromCsv(string csv, bool containsHeader)
        {
            var list = new List<PeRatio>();
            using (var reader = new CsvReader(new StringReader(csv), containsHeader))
            {
                if (reader.FieldCount == 4)
                {
                    while (reader.ReadNextRecord())
                    {
                        list.Add(new PeRatio
                                     {
                                         Symbol = reader[(int) PeRatio.ColumnSerializationIndices.Symbol].Trim().ToUpperInvariant(),
                                         EffectiveDate = DateTime.ParseExact(reader[(int) PeRatio.ColumnSerializationIndices.EffectiveDate], GlobalOptions.Instance.CsvDateFormat, CultureInfo.InvariantCulture),
                                         PeLatest = decimal.Parse(reader[(int) PeRatio.ColumnSerializationIndices.PeLatest]),
                                         PeAudited = decimal.Parse(reader[(int) PeRatio.ColumnSerializationIndices.PeAudited])
                                     });
                    }
                }
            }

            return createPeRatiosList(list);
        }

        public static PeRatiosList FromXml(string xml)
        {
            List<PeRatio> list;

            using (var stringReader = new StringReader(xml))
            {
                using (var xmlReader = new XmlTextReader(stringReader))
                {
                    var serializer = new XmlSerializer(typeof (List<PeRatio>));
                    list = (List<PeRatio>) serializer.Deserialize(xmlReader);
                }
            }

            return createPeRatiosList(list);
        }

        public static PeRatiosList FromJson(string json)
        {
            return createPeRatiosList(JsonConvert.DeserializeObject<List<PeRatio>>(json));
        }

        public static PeRatiosList FromProtobuf(byte[] proto)
        {
            List<PeRatio> list;

            using (var stream = new MemoryStream())
            {
                stream.Write(proto, 0, proto.Length);
                stream.Seek(0, SeekOrigin.Begin);

                list = Serializer.Deserialize<List<PeRatio>>(stream);
            }

            return createPeRatiosList(list);
        }
    }
}